package com.fuyao.cloud.admin.flow.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fuyao.cloud.admin.api.entity.SysUser;
import com.fuyao.cloud.admin.flow.constant.FlowConstant;
import com.fuyao.cloud.admin.flow.dto.*;
import com.fuyao.cloud.admin.flow.service.flowable.ActorUserService;
import com.fuyao.cloud.admin.flow.util.FlowableHelper;
import com.fuyao.cloud.admin.service.SysUserService;
import com.fuyao.cloud.common.core.util.R;
import com.fuyao.cloud.common.security.util.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.*;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
@Api(value = "flowable", tags = "流程运行时操作")
@RequestMapping("/flowable/context")
public class FlowableContextController {
    private final RepositoryService repositoryService;
    private final TaskService taskService;
    private final HistoryService historyService;
    private final SysUserService sysUserService;
    private final ActorUserService actorUserService;
    private final RuntimeService runtimeService;

    @ApiOperation(value = "根据 businessKey 查询当前用户办理环节上下文信息")
    @ApiImplicitParam(name = "businessKey", required = true, value = "businessKey", defaultValue = "123456", dataTypeClass = String.class, paramType = "path")
    @GetMapping("/getFlowContext/{businessKey}")
    public R<FlowContextInfo> getFlowContext(@PathVariable("businessKey") String businessKey, String taskId) {
        FlowContextInfo flowContext = new FlowContextInfo();

        if (taskId != null) { // 尝试使用任务 id 获取历史已办
            List<HistoricTaskInstance> historicTaskInstances = historyService
                    .createHistoricTaskInstanceQuery()
                    .taskAssignee(SecurityUtils.getUser().getUsername())
                    .finished()
                    .taskId(taskId)
                    .list()
                    .stream()
                    // 过滤掉标记为退回,提取的已办
                    .filter(taskInstance -> !StrUtil.containsAny(taskInstance.getDeleteReason(), FlowConstant.WITHDRAW_DELETE_REASON, FlowConstant.REJECT_DELETE_REASON))
                    .collect(Collectors.toList());

            HistoricTaskInstance historicTaskInstance = null;
            if (CollUtil.isNotEmpty(historicTaskInstances)) {
                historicTaskInstance = historicTaskInstances.get(0);
            }

            if (historicTaskInstance != null) {
                // 获取流程变量
                Map<String, Object> variables = new HashMap<>();
                historyService
                        .createHistoricVariableInstanceQuery()
                        .processInstanceId(historicTaskInstance.getProcessInstanceId())
                        .list()
                        .forEach(variable -> variables.put(variable.getVariableName(), variable.getValue()));

                // 获取下一环节
                List<UserTask> nextUserTasks = FlowableHelper.findNextNodes(repositoryService, historicTaskInstance, variables);
                if (!nextUserTasks.isEmpty()) {
                    Set<String> nextActivityIds = nextUserTasks.stream().map(BaseElement::getId).collect(Collectors.toSet());
                    // 获取下一环节的已办
                    List<HistoricTaskInstance> nodeHistoricTaskInstances = historyService
                            .createHistoricTaskInstanceQuery()
                            .processInstanceBusinessKey(businessKey)
                            .taskDefinitionKeys(nextActivityIds)
                            .finished()
                            .list()
                            .stream()
                            // 过滤掉标记为退回,提取的已办
                            .filter(taskInstance -> !StrUtil.containsAny(taskInstance.getDeleteReason(), FlowConstant.WITHDRAW_DELETE_REASON, FlowConstant.REJECT_DELETE_REASON))
                            .collect(Collectors.toList());
                    if (nodeHistoricTaskInstances.isEmpty()) { // 没有正常完成的已办说明可以提取
                        flowContext.setCanWithdraw(true);
                    }
                    flowContext.setTaskId(taskId);
                    flowContext.setId(historicTaskInstance.getTaskDefinitionKey());
                    flowContext.setName(historicTaskInstance.getName());
                }
                return R.ok(flowContext);
            }
        }

        Task task = FlowableHelper.getTask(taskService, taskId, businessKey);
        if (task == null) {
            return R.ok(flowContext);
        }
        String processInstanceId = task.getProcessInstanceId();

        // 获取环节变量
        Map<String, Object> variables = taskService.getVariables(task.getId());

        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        String taskDefinitionKey = task.getTaskDefinitionKey();
        Map<String, FlowElement> flowElementsMap = FlowableHelper.getAllFlowElementsMap(bpmnModel);
        FlowElement flowElement = flowElementsMap.get(taskDefinitionKey);

        List<UserTask> nextUserTasks = FlowableHelper.findNextNodes(flowElementsMap, flowElementsMap.get(taskDefinitionKey), variables);
        boolean hasNext = nextUserTasks.size() > 0;

        flowContext.setHasNext(hasNext);

        Set<SequenceFlow> incoming = FlowableHelper.getIncoming(flowElement);
        // 有待办就可以退回
        flowContext.setCanReject(true);
        for (SequenceFlow sequenceFlow : incoming) {
            if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) {
                // 开始环节不能出现退回
                flowContext.setCanReject(false);
                flowContext.setStartNode(true);
            }
        }

        // 当前环节
        UserTask currentUserTask = FlowableHelper.taskToUserTask(repositoryService, task);
        if (currentUserTask != null) {
            Set<Handler> assigneeUsers = FlowableHelper.getExtension(currentUserTask, "assigneeUsers", Handler.class);
            flowContext.setVariables(taskService.getVariables(task.getId()));
            flowContext.setTaskId(task.getId());
            flowContext.setName(currentUserTask.getName());
            flowContext.setId(currentUserTask.getId());
            flowContext.setHandlers(assigneeUsers);
            setExtension(flowContext, currentUserTask, processInstanceId, hasNext, variables);
        }

        if (hasNext) {
            Set<FlowNodeInfo> nextNodes = new HashSet<>();
            nextUserTasks.forEach(userTask -> {
                FlowNodeInfo flowNodeInfo = new FlowNodeInfo(userTask);
                setExtension(flowNodeInfo, userTask, processInstanceId, true, variables);
                nextNodes.add(flowNodeInfo);
            });
            flowContext.setNextNodes(nextNodes);
        }

        return R.ok(flowContext);
    }

    private void setExtension(FlowNodeInfo flowNodeInfo, UserTask userTask, String processInstanceId, boolean hasNext,Map<String, Object> variables) {
        Set<String> enabledFields = FlowableHelper.getExtension(userTask, "enabledFields", String.class);
        Set<String> hideFields = FlowableHelper.getExtension(userTask, "hideFields", String.class);
        Set<String> enabledButtons = FlowableHelper.getExtension(userTask, "enabledButtons", String.class);

        boolean containEndNode = false;
        Set<SequenceFlow> sequenceFlows = FlowableHelper.getOutgoing(userTask, variables);
        for (SequenceFlow sequenceFlow : sequenceFlows) {
            if (sequenceFlow.getTargetFlowElement() instanceof EndEvent) {
                containEndNode = true;
                break;
            }
        }

        if (hasNext) {
            enabledButtons.add("submit");
            if (containEndNode) {
                enabledButtons.add("finish");
            }
        } else {
            enabledButtons.add("finish");
        }

        Set<String> disabledButtons = FlowableHelper.getExtension(userTask, "disabledButtons", String.class);

        flowNodeInfo.setLoopCharacteristics(userTask.getLoopCharacteristics() != null);
        flowNodeInfo.setEnabledFields(enabledFields);
        flowNodeInfo.setHideFields(hideFields);
        flowNodeInfo.setEnabledButtons(enabledButtons);
        flowNodeInfo.setDisabledButtons(disabledButtons);
        flowNodeInfo.setHandlers(FlowableHelper.getNodeHandlers(sysUserService, actorUserService, processInstanceId, userTask));
    }

    @ApiOperation(value = "获取可退回的点")
    @ApiImplicitParam(name = "taskId", required = true, value = "taskId", defaultValue = "123456", dataTypeClass = String.class, paramType = "path")
    @GetMapping("/getBackNodes/{taskId}")
    public R<List<BackFlowNode>> getBackNodes(@PathVariable("taskId") String taskId) {
        Task task = FlowableHelper.getTask(taskService, taskId);
        if (task != null) {

            BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
            StartEvent from = FlowableHelper.getStartFlowElement(bpmnModel.getMainProcess().getFlowElements());
            FlowNode to = (FlowNode) bpmnModel.getFlowElement(task.getTaskDefinitionKey());
            Map<String, Object> variables = runtimeService.getVariables(task.getProcessInstanceId());
            List<List<FlowElement>> paths = FlowableHelper.findPathsDFS(from, to, variables);
            Set<String> allowNodeIds = paths.stream()
                    .flatMap(Collection::stream)
                    .filter(flowElement -> flowElement instanceof UserTask)
                    .map(BaseElement::getId)
                    .collect(Collectors.toSet());

            List<BackFlowNode> backFlowNodes = new ArrayList<>();
            Set<String> nodeIds = new HashSet<>();
            historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId()).activityType(BpmnXMLConstants.ELEMENT_TASK_USER)
                    .orderByHistoricActivityInstanceEndTime()
                    .desc()
                    .finished()
                    .list()
                    .forEach(historicActivityInstance -> {
                        if (!nodeIds.contains(historicActivityInstance.getActivityId()) && allowNodeIds.contains(historicActivityInstance.getActivityId()) && !task.getTaskDefinitionKey().equals(historicActivityInstance.getActivityId())) {
                            nodeIds.add(historicActivityInstance.getActivityId());
                            Set<String> handlerNames = FlowableHelper
                                    .getNodeDoneHistoricTaskInstancesByProcessInstanceId(historyService, task.getProcessInstanceId(), Collections.singletonList(historicActivityInstance.getActivityId()))
                                    .stream()
                                    .map(TaskInfo::getAssignee)
                                    .collect(Collectors.toSet());

                            backFlowNodes.add(BackFlowNode.builder()
                                    .id(historicActivityInstance.getActivityId())
                                    .name(historicActivityInstance.getActivityName())
                                    .handlers(getHandlers(handlerNames))
                                    .build());
                        }
                    });
            return R.ok(backFlowNodes);
        } else {
            return R.failed("获取不到任务,请关闭页面后重试!");
        }
    }

    private Set<ActivityVo.Handler> getHandlers(Set<String> usernames) {
        if (CollUtil.isNotEmpty(usernames)) {
            Set<ActivityVo.Handler> handlers = new HashSet<>();
            List<SysUser> sysUsers = sysUserService.findUsesByUserNames(new ArrayList<>(usernames));
            sysUsers.forEach(sysUser -> handlers.add(ActivityVo.Handler.builder()
                    .username(sysUser.getUsername())
                    .id(sysUser.getUserId())
                    .name(sysUser.getName())
                    .build()));
            return handlers;
        }
        return Collections.emptySet();
    }
}
